package vip.aning.gitsync.service;

import javafx.concurrent.Task;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import vip.aning.gitsync.config.ConfigManager;
import vip.aning.gitsync.config.ThreadPoolManager;
import vip.aning.gitsync.constant.AppConst;
import vip.aning.gitsync.data.SyncMessageListData;
import vip.aning.gitsync.model.GitInfo;
import vip.aning.gitsync.utils.DatetimeUtil;
import vip.aning.gitsync.utils.GitUtils;

import java.io.File;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * GitClientService
 */
@Slf4j
public class GitClientService {
    private final org.slf4j.Logger LOG = LoggerFactory.getLogger(this.getClass());
    // 获取线程池单例
    private final ThreadPoolManager threadPoolManager = ThreadPoolManager.getInstance();
    private final GitInfo gitInfo = ConfigManager.getInstance().getGitInfo();
    ;
    private final GitUtils gitUtils;

    public GitClientService() {
        this.gitUtils = new GitUtils(gitInfo.getUsername(), gitInfo.getPassword(),
                gitInfo.getAuthorName(), gitInfo.getAuthorEmail());
    }

    /**
     * 克隆远程库
     */
    public void cloneRepository(StackPane loadingPane, Label loadingLabel) {
        loadingPane.setVisible(true);
        loadingLabel.setText("克隆中....");
        // 创建一个长时间任务
        Task<String> task = new Task<>() {
            @Override
            protected String call() throws Exception {
                // 模拟长时间任务
                gitUtils.cloneRepository(gitInfo.getRepoUrl(), gitInfo.getBranch(), gitInfo.getLocalPath());
                return AppConst.success;
            }
        };
        // 任务完成后隐藏加载提示
        task.setOnSucceeded(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("克隆完成....");
            SyncMessageListData.add(AppConst.MsgType.LOG, "克隆完成，请查看[" + gitInfo.getLocalPath() + "]目录");
        });

        task.setOnFailed(event -> {
            if (event.getSource().getException()
                    .getMessage()
                    .contains("already exists and is not an empty directory")) {
                SyncMessageListData.add(AppConst.MsgType.ERROR, "同步目录[" + gitInfo.getLocalPath() + "]不是空的目录，停止同步。如需克隆项目请选择空的目录。");
            }
            loadingPane.setVisible(false);
            loadingLabel.setText("任务失败！");
        });

        // 提交任务到线程池
        Future<?> future = threadPoolManager.execute(task);

        // 启动一个线程监控超时
        new Thread(() -> {
            try {
                future.get(30 * 60, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                SyncMessageListData.add(AppConst.MsgType.ERROR, "克隆...超时，请重试");
                task.cancel(); // 超时后取消任务
                loadingPane.setVisible(false);
            } catch (InterruptedException | ExecutionException e) {
                LOG.error(e.getMessage(), e);
            }
        }).start();
    }

    public void syncWithRemote() {
        // 创建一个长时间任务
        Task<Void> task = new Task<>() {
            @Override
            protected Void call() throws Exception {
                File localDir = new File(gitInfo.getLocalPath());
                // 检查本地目录是否存在
                if (!localDir.exists() || !localDir.isDirectory()) {
                    throw new Exception("本地目录不存在或不是有效的目录。");
                }
                // 检查本地目录是否为空则克隆给定的git仓库
                if (localDir.listFiles() == null || Objects.requireNonNull(localDir.listFiles()).length == 0) {
                    gitUtils.cloneRepository(gitInfo.getRepoUrl(), gitInfo.getBranch(), gitInfo.getLocalPath());
                }

                LOG.info("拉取一次远程文件。");
                gitUtils.pullAndPrintChanges(gitInfo.getLocalPath(), gitInfo.getBranch());

                // 模拟长时间任务
                if (gitUtils.hasLocalChanges(gitInfo.getLocalPath())) {
                    LOG.info("打印本地变动：");
                    gitUtils.printChangedFiles(gitInfo.getLocalPath());

                    LOG.info("提交本地变动文件并推送远程仓库...");
                    gitUtils.commitAndPush(gitInfo.getLocalPath(), gitInfo.getCommitMessage(), gitInfo.getSelectLocalOrRemote());
                }
                return null;
            }
        };
        // 任务完成后隐藏加载提示
        task.setOnSucceeded(event -> {
            SyncMessageListData.add(AppConst.MsgType.LOG, DatetimeUtil.getCurrentDateTime() + " 同步完成，请查看[" + gitInfo.getLocalPath() + "]目录");
        });

        task.setOnFailed(event -> {
            String errInfo = event.getSource().getException().getMessage();
            LOG.error("同步失败，可能的原因：{}", errInfo);
            SyncMessageListData.add(AppConst.MsgType.ERROR, DatetimeUtil.getCurrentDateTime() + " 同步失败，可能的原因：" + errInfo);
        });
        // 提交任务到线程池
        threadPoolManager.execute(task);
    }

    /**
     * 本地提交代码
     */
    public void commit() {
        threadPoolManager.execute(() -> {
            try {
                gitUtils.commit(gitInfo.getLocalPath(), gitInfo.getCommitMessage(), gitInfo.getSelectLocalOrRemote());
            } catch (Exception e) {
                LOG.error("commit error", e);
            }
        });
    }

    /**
     * 拉取远程仓库内容到本地
     */
    public void pull() {
        threadPoolManager.execute(() -> {
            try {
                gitUtils.pull(gitInfo.getLocalPath());
            } catch (Exception e) {
                LOG.error("pull error", e);
            }
        });
    }

    /**
     * push本地代码到远程仓库地址
     */
    public void push() {
        threadPoolManager.execute(() -> {
            try {
                gitUtils.push(gitInfo.getLocalPath());
            } catch (Exception e) {
                LOG.error("push error", e);
            }
        });
    }

    /**
     * 检查本地路径是否是有效的 Git 仓库
     *
     * @return true 如果是有效的 Git 仓库，否则 false
     */
    public void isValidRepository(StackPane loadingPane, Label loadingLabel) {
        loadingPane.setVisible(true);
        loadingLabel.setText("校验仓库有效性....");
        // 创建一个长时间任务
        Task<String> task = new Task<>() {
            @Override
            protected String call() throws Exception {
                // 模拟长时间任务
                if (!gitUtils.isValidRepository(gitInfo.getLocalPath())) {
                    throw new Exception("选择的目录不是有效的git仓库");
                }
                return AppConst.success;
            }
        };
        // 任务完成后隐藏加载提示
        task.setOnSucceeded(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("仓库校验完成....仓库有效");
            SyncMessageListData.add(AppConst.MsgType.LOG, "校验仓库有效性完成，[" + gitInfo.getLocalPath() + "]仓库正常");

            isValidUser(loadingPane, loadingLabel);
        });

        task.setOnFailed(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("仓库校验完成");
            SyncMessageListData.add(AppConst.MsgType.ERROR, "校验仓库有效性完成，[" + gitInfo.getLocalPath() + "]不是有效的git仓库");
        });

        // 提交任务到线程池
        Future<?> future = threadPoolManager.execute(task);

        // 启动一个线程监控超时
        new Thread(() -> {
            try {
                future.get(30, TimeUnit.SECONDS); // 设置超时时间为 30 秒
            } catch (TimeoutException e) {
                SyncMessageListData.add(AppConst.MsgType.ERROR, "校验仓库有效性....超时，请重试");
                task.cancel(); // 超时后取消任务
                loadingPane.setVisible(false);
            } catch (InterruptedException | ExecutionException e) {
                LOG.error(e.getMessage(), e);
            }
        }).start();
    }

    /**
     * 检查用户凭据是否正确
     *
     * @return true 如果用户凭据正确，否则 false
     */
    public void isValidUser(StackPane loadingPane, Label loadingLabel) {
        loadingPane.setVisible(true);
        loadingLabel.setText("校验用户名和密码....");
        // 创建一个长时间任务
        Task<String> task = new Task<>() {
            @Override
            protected String call() throws Exception {
                // 模拟长时间任务
                if (!gitUtils.isValidUser(gitInfo.getRepoUrl())) {
                    throw new Exception("用户名或密码错误");
                }
                return AppConst.success;
            }
        };
        // 任务完成后隐藏加载提示
        task.setOnSucceeded(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("校验用户名和密码....用户正常");
            SyncMessageListData.add(AppConst.MsgType.LOG, "校验用户名和密码完成，[" + gitInfo.getUsername() + "]用户名和密码正确");

            isRemoteBranchExists(loadingPane, loadingLabel);
        });

        task.setOnFailed(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("校验用户名和密码完成");
            SyncMessageListData.add(AppConst.MsgType.ERROR, "校验用户名和密码完成，[" + gitInfo.getUsername() + "]用户名或密码错误，也可能是网络问题，请重新尝试。");
        });

        // 提交任务到线程池
        Future<?> future = threadPoolManager.execute(task);

        // 启动一个线程监控超时
        new Thread(() -> {
            try {
                future.get(30, TimeUnit.SECONDS); // 设置超时时间为 30 秒
            } catch (TimeoutException e) {
                SyncMessageListData.add(AppConst.MsgType.ERROR, "校验用户名和密码....超时，请重试");
                task.cancel(); // 超时后取消任务
                loadingPane.setVisible(false);
            } catch (InterruptedException | ExecutionException e) {
                LOG.error(e.getMessage(), e);
            }
        }).start();
    }

    public void isRemoteBranchExists(StackPane loadingPane, Label loadingLabel) {
        loadingPane.setVisible(true);
        loadingLabel.setText("检查远程是否有[" + gitInfo.getBranch() + "]分支....");
        // 创建一个长时间任务
        Task<String> task = new Task<>() {
            @Override
            protected String call() throws Exception {
                if (!gitUtils.isRemoteBranchExists(gitInfo.getRepoUrl(), gitInfo.getBranch())) {
                    throw new Exception("远程没有[" + gitInfo.getBranch() + "]分支");
                }
                return AppConst.success;
            }
        };
        // 任务完成后隐藏加载提示
        task.setOnSucceeded(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("检查远程是否有[" + gitInfo.getBranch() + "]分支....正常");
            SyncMessageListData.add(AppConst.MsgType.LOG, "检查远程是否有[" + gitInfo.getBranch() + "]分支....存在相应分支");
        });

        task.setOnFailed(event -> {
            loadingPane.setVisible(false);
            loadingLabel.setText("校验用户名和密码完成");
            // 获取异常信息
            String errInfo = event.getSource().getException().getMessage();
            SyncMessageListData.add(AppConst.MsgType.ERROR, errInfo);
        });

        // 提交任务到线程池
        Future<?> future = threadPoolManager.execute(task);

        // 启动一个线程监控超时
        new Thread(() -> {
            try {
                future.get(30, TimeUnit.SECONDS); // 设置超时时间为 30 秒
            } catch (TimeoutException e) {
                SyncMessageListData.add(AppConst.MsgType.ERROR, "检查远程是否有[" + gitInfo.getBranch() + "]分支....超时，请重试");
                task.cancel(); // 超时后取消任务
                loadingPane.setVisible(false);
            } catch (InterruptedException | ExecutionException e) {
                LOG.error(e.getMessage(), e);
            }
        }).start();
    }
}
